/*
 * This software code is (c) 2010 T-Mobile USA, Inc. All Rights Reserved.
 *
 * Unauthorized redistribution or further use of this material is
 * prohibited without the express permission of T-Mobile USA, Inc. and
 * will be prosecuted to the fullest extent of the law.
 *
 * Removal or modification of these Terms and Conditions from the source
 * or binary code of this software is prohibited.  In the event that
 * redistribution of the source or binary code for this software is
 * approved by T-Mobile USA, Inc., these Terms and Conditions and the
 * above copyright notice must be reproduced in their entirety and in all
 * circumstances.
 *
 * No name or trademarks of T-Mobile USA, Inc., or of its parent company,
 * Deutsche Telekom AG or any Deutsche Telekom or T-Mobile entity, may be
 * used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" AND "WITH ALL FAULTS" BASIS
 * AND WITHOUT WARRANTIES OF ANY KIND.  ALL EXPRESS OR IMPLIED
 * CONDITIONS, REPRESENTATIONS OR WARRANTIES, INCLUDING ANY IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT CONCERNING THIS SOFTWARE, ITS SOURCE OR BINARY CODE
 * OR ANY DERIVATIVES THEREOF ARE HEREBY EXCLUDED.  T-MOBILE USA, INC.
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE
 * OR ITS DERIVATIVES.  IN NO EVENT WILL T-MOBILE USA, INC. OR ITS
 * LICENSORS BE LIABLE FOR LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES,
 * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT
 * OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF T-MOBILE USA,
 * INC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * THESE TERMS AND CONDITIONS APPLY SOLELY AND EXCLUSIVELY TO THE USE,
 * MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE, ITS SOURCE OR BINARY
 * CODE OR ANY DERIVATIVES THEREOF, AND ARE SEPARATE FROM ANY WRITTEN
 * WARRANTY THAT MAY BE PROVIDED WITH A DEVICE YOU PURCHASE FROM T-MOBILE
 * USA, INC., AND TO THE EXTENT PERMITTED BY LAW.
 */

package javax.microedition.ims.core.sipservice;

import javax.microedition.ims.common.Logger;
import javax.microedition.ims.common.Protocol;
import javax.microedition.ims.common.Shutdownable;
import javax.microedition.ims.core.transaction.*;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class TransactionState<T extends Transaction<Boolean, M>, M> implements Shutdownable {
    protected enum RequestState {
        PREACCEPTED, ACCEPTED, REJECTED, CANCELED, TIMEOUT, PRACKED, ACCEPTED_UPDATE, REJECTED_UPDATE
    }

    protected final T transaction;
    private final TransactionListener<M> lifeCycleListener;
    private final AtomicBoolean firstInitComplete = new AtomicBoolean(false);

    public TransactionState(final T transaction) {
        this.transaction = transaction;

        transaction.addListener(
                lifeCycleListener = new TransactionListenerAdapter<M>() {

                    public void onTransactionComplete(final TransactionEvent<M> event, final TransactionResult.Reason reason) {
                        event.getTransaction().removeListener(this);
                        shutdown();
                    }
                }
        );
    }

    public void onStateInitiated(final TransactionStateChangeEvent<M> triggeringEvent) {

        if (firstInitComplete.compareAndSet(false, true)) {
            onFirstInit(triggeringEvent);
        }

        final TransactionStateChangeEvent<M> event =
                createStateChangeEvent(
                        triggeringEvent.getStateChangeReason(),
                        triggeringEvent.getTriggeringMessage()
                );

        ((CommonTransaction) transaction).notifyTU(event);

    }

    protected abstract void onFirstInit(final TransactionStateChangeEvent<M> triggeringEvent);

    public void onMessageReceived(M msg) {
    }

    public void onStateCompleted() {
        transaction.removeListener(lifeCycleListener);
    }

    public void onTUReceived(TUEvent event) {
    }

    protected abstract State getTransactionStateName();

    protected void log(String msg, String prefix) {
        Logger.log(prefix, msg);
    }


    public String toString() {

        final String wholeName = this.getClass().getName();
        String[] nameParts = wholeName.split("\\.");
        String shortName = nameParts == null || nameParts.length == 0 ? wholeName : nameParts[nameParts.length - 1];

        final StringBuffer sb = new StringBuffer();
        sb.append(shortName);
        sb.append(" ").append(transaction);
        return sb.toString();
    }

    public String shortName() {

        final String wholeName = this.getClass().getName();
        String[] nameParts = wholeName.split("\\.");
        String shortName = nameParts == null || nameParts.length == 0 ? wholeName : nameParts[nameParts.length - 1];

        return this.getClass().getSimpleName();
    }


    protected boolean isUnReliableProtocol() {
        //return transaction.getStackContext().getConfig().getConnectionType() == Protocol.UDP;
        return transaction.getStackContext().getConnectionType() == Protocol.UDP;
    }

    protected boolean isReliableProtocol() {
        //return transaction.getStackContext().getConfig().getConnectionType() == Protocol.TCP;
        return transaction.getStackContext().getConnectionType() == Protocol.TCP;
    }

    protected TransactionStateChangeEvent<M> createStateChangeEvent(
            final StateChangeReason reason,
            final M triggeringMessage) {

        return new DefaultTransactionStateChangeEvent<M>(
                transaction,
                getTransactionStateName(),
                reason,
                triggeringMessage
        );
    }

    public void shutdown() {
        onStateCompleted();

        final TransactionStateChangeEvent<M> event =
                createStateChangeEvent(
                        StateChangeReason.TRANSACTION_SHUTDOWN,
                        null
                );

        ((CommonTransaction) transaction).notifyTU(event);
    }
}
